// MyFamily - XML based genealogy program.
// Copyright (C) 2006  Doug Swisher
// See the file COPYING.txt for license details.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using NUnit.Framework;


namespace DataModel
{
    public class FamilyIndividual : IComparable
    {
        private string id;
        private List<FamilyPersona> personas = new List<FamilyPersona>();


        public FamilyIndividual(string id)
        {
            this.id = id;
        }


        public void AddPersona(FamilyPersona persona)
        {
            this.personas.Add(persona);
            persona.Individual = this;
        }


        public void SaveXml(XmlWriter writer)
        {
            writer.WriteStartElement("individual");
            writer.WriteAttributeString("id", this.id);

            foreach (FamilyPersona persona in this.personas)
            {
                writer.WriteStartElement("persona");
                writer.WriteAttributeString("id", persona.Id);
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }


        public void ParseXml(FamilyInfo info, XmlElement individualElement)
        {
            // Link up with the personas...
            foreach (XmlElement personaElement in individualElement.SelectNodes("persona"))
            {
                string personaId = personaElement.GetAttribute("id");

                // TODO - Doug - 8/27/2006 - need to handle the case where the individual is parsed before persona; or
                // do we always assume the persona is present and emit an error if we can't find them?  My thought is
                // that if we have a bad reference, we'll never know until its too late...unless we go back through and
                // look for "empty" personas...
                if (info.Personas.ContainsKey(personaId))
                {
                    FamilyPersona persona = info.Personas[personaId];

                    this.personas.Add(persona);
                    persona.Individual = this;
                }
            }
        }


        public PersonalName Name
        {
            get
            {
                // TODO - Doug - 8/2/2006 - choose the persona with the "best" name....
                // TODO - Doug - 8/4/2006 - Perhaps this should be set when the persona is added?
                if (this.personas.Count > 0)
                {
                    return this.personas[0].Name;
                }
                else
                {
                    return PersonalName.Unknown;
                }
            }
        }


        public FamilyIndividual Mother
        {
            get
            {
                // TODO - Doug - 8/4/2006 - choose the persona with the "best" mother....
                // TODO - Doug - 8/4/2006 - Perhaps this should be set when the persona is added?
                if ((this.personas.Count > 0) && (this.personas[0].Mother != null))
                {
                    return this.personas[0].Mother.Individual;
                }
                else
                {
                    return null;
                }
            }
        }


        public FamilyIndividual Father
        {
            get
            {
                // TODO - Doug - 8/4/2006 - choose the persona with the "best" father....
                // TODO - Doug - 8/4/2006 - Perhaps this should be set when the persona is added?
                if ((this.personas.Count > 0) && (this.personas[0].Father != null))
                {
                    return this.personas[0].Father.Individual;
                }
                else
                {
                    return null;
                }
            }
        }


        public List<FamilyIndividual> Children
        {
            get
            {
                List<FamilyIndividual> list = new List<FamilyIndividual>();

                // TODO - Doug - 8/4/2006 - Perhaps this should be set when the persona is added?
                if (this.personas.Count > 0)
                {
                    foreach (FamilyPersona child in this.personas[0].Children)
                    {
                        list.Add(child.Individual);
                    }
                }

                return list;
            }
        }


        public List<FamilyEvent> Events
        {
            get
            {
                List<FamilyEvent> list = new List<FamilyEvent>();

                foreach (FamilyPersona persona in this.personas)
                {
                    foreach (FamilyEvent eve in persona.Events)
                    {
                        list.Add(eve);
                    }

                }

                return list;
            }
        }


        public List<FamilyPersona> Personas
        {
            get { return this.personas; }
        }


        public string Id
        {
            get
            {
                return this.id;
            }
        }


        public string LifeSpan
        {
            get
            {
                // TODO - Doug - 9/2/2006 - implement LifeSpan calculation...
                return "(19xx-19xx)";
            }
        }



        public override bool Equals(object obj)
        {
            // Simple cases...
            if (obj == null)
            {
                return false;
            }

            FamilyIndividual other = obj as FamilyIndividual;

            if (other == null)
            {
                return false;
            }

            // Check members...
            if (!string.Equals(this.id, other.id))
            {
                return false;
            }

            if (this.personas.Count != other.personas.Count)
            {
                return false;
            }

            foreach (FamilyPersona thisPersona in this.personas)
            {
                // TODO - Doug - 8/27/2006 - make this more efficient (leery of making this.personas a dictionary just for this)
                bool found = false;
                foreach (FamilyPersona otherPersona in other.personas)
                {
                    if (thisPersona.Id == otherPersona.Id)
                    {
                        found = true;
                    }
                }

                if (!found)
                {
                    return false;
                }
            }

            return true;
        }



        public override int GetHashCode()
        {
            // TODO - Doug - 8/27/2006 - do we want to change this implementation at all?
            return base.GetHashCode();
        }



        public int CompareTo(object obj)
        {
            FamilyIndividual other = obj as FamilyIndividual;
            if (other == null)
            {
                return 0;
            }

            if (this.Name == null)
            {
                if (other.Name == null)
                {
                    return 0;
                }
                else
                {
                    return -1;
                }
            }
            else
            {
                if (other.Name == null)
                {
                    return 1;
                }
                else
                {
                    return this.Name.CompareTo(other.Name);
                }
            }
        }

    }



    [TestFixture]
    public class IndividualEquality
    {
        private static FamilyExtract extract = new FamilyExtract("extract", new FamilySource("ExtractSource"));

        [Test]
        public void Nulls()
        {
            FamilyIndividual indi1 = new FamilyIndividual("1");

            Assert.AreNotEqual(null, indi1);
            Assert.AreNotEqual(indi1, null);
        }


        [Test]
        public void SameIds()
        {
            FamilyIndividual indi1 = new FamilyIndividual("1");
            FamilyIndividual indi2 = new FamilyIndividual("1");

            Assert.AreEqual(indi1, indi2);
            Assert.AreEqual(indi2, indi1);
        }



        [Test]
        public void DifferentIds()
        {
            FamilyIndividual indi1 = new FamilyIndividual("1");
            FamilyIndividual indi2 = new FamilyIndividual("2");

            Assert.AreNotEqual(indi1, indi2);
            Assert.AreNotEqual(indi2, indi1);
        }



        [Test]
        public void DifferentPersonaCount()
        {
            FamilyIndividual indi1 = new FamilyIndividual("1");
            FamilyIndividual indi2 = new FamilyIndividual("1");

            indi1.Personas.Add(new FamilyPersona(extract, "2"));

            Assert.AreNotEqual(indi1, indi2);
            Assert.AreNotEqual(indi2, indi1);
        }


        [Test]
        public void DifferentPersonas()
        {
            FamilyIndividual indi1 = new FamilyIndividual("1");
            FamilyIndividual indi2 = new FamilyIndividual("1");

            indi1.Personas.Add(new FamilyPersona(extract, "1"));
            indi2.Personas.Add(new FamilyPersona(extract, "2"));

            Assert.AreNotEqual(indi1, indi2);
            Assert.AreNotEqual(indi2, indi1);
        }


        [Test]
        public void SamePersonas()
        {
            FamilyIndividual indi1 = new FamilyIndividual("1");
            FamilyIndividual indi2 = new FamilyIndividual("1");

            indi1.Personas.Add(new FamilyPersona(extract, "1"));
            indi2.Personas.Add(new FamilyPersona(extract, "1"));

            Assert.AreEqual(indi1, indi2);
            Assert.AreEqual(indi2, indi1);
        }

    }
}
